<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * 权限类
 * 
 * @author  : $Author: spike $
 * @date    : $Date: 2014-04-30 09:50:32 +0800 (三, 2014-04-30) $
 * @version : $Id: rbac_model.php 89 2014-04-30 01:50:32Z spike $
 * @rev     : $Revision: 89 $
 */
class Rbac_model extends CI_Model
{
    private static $_salt      = "^salt tastes salty^";
    private static $_max_depth = 3;

    public function __construct()
    {
        parent::__construct();
    }

    // --------------------------------------------------------------------

    /**
     * [登陆]
     * @param  [string] $username [用户名]
     * @param  [string] $password [密码] 
     * @param  [bool]   $remember [是否保存用户名到cookie] 
     * @return [bool] 
     */
    public function login($username, $password, $remember = FALSE)
    {
        if( FALSE === $admin = $this->get_account($username, $password))
        {
            return FALSE;
        }

        $now = now_str();
        $this->load->dao('Admin_dao');
        $this->Admin_dao->update(
            array('last_login'=>$now),
            array('admin_id'=>$admin['admin_id'], 'is_delete' => UNDELETED),
            NULL,
            1
        );

        $_SESSION['login_info'] = array(
            'admin_id'       => $admin['admin_id'],
            'username'       => $admin['username'],
            'nickname'       => $admin['nickname'],
            'email'          => $admin['email'],
            'resource'       => $admin['resource'],
            'avatar_version' => $admin['avatar_version'],
            'login_time'     => $now,
        );

        if( $remember === TRUE )
        {
            $this->input->set_cookie('username', $username, 86400*30);
        }

        return TRUE;
    }

    /**
     * [登出]
     */
    public function logout()
    {
        unset($_SESSION);
        session_destroy();
    }

    // --------------------------------------------------------------------

    /**
     * [获取账户]
     * @param  [string] $username [用户名]
     * @param  [string] $password [密码]
     * @return [array/bool] 成功返回账户信息
     */
    public function get_account($username, $password)
    {
        if( ! $this->val_username($username))
        {
            $this->set_error("验证账户失败：".$this->get_error());
            return FALSE;
        }
        if( ! $this->val_password($password))
        {
            $this->set_error("验证账户失败：".$this->get_error());
            return FALSE;
        }
        $account = array(
            'username' => $username, 
            'password' => $this->_encrypt_password($password), 
            'status'   => ADMIN_STATUS_ON,
            'is_delete'=> UNDELETED,
        );
        $this->load->dao('Admin_dao');
        $admin = $this->Admin_dao->get_one($account);

        if( ! $admin)
        {
            $this->set_error("获取账户失败: 账户不存在或密码不正确。");
            return FALSE;
        }
    
        if(
            ! isset($admin['admin_id']) OR 
            ! isset($admin['username'])
        )
        {
            $this->set_error("获取账户失败: 账户资料不完整。");
            return FALSE;
        }

        // 获得角色所拥有的资源
        $admin['resource'] = $this->get_resource($admin['admin_id']);
        
        return $admin;
    }

    /**
     * [获得多个角色的所有权限&菜单资源]
     * @param  [string/array]   $role_ids      [逗号分隔的字符串或数组]
     * @return [array]
     */
    public function get_resource($admin_id)
    {
        $resource = array(
            'priv' => array(),
            'menu' => array(),
        );
        $admin_id = (int)$admin_id;

        // 获取用户拥有角色
        $this->load->dao('Role_admin_dao');
        $role_ids = $this->Role_admin_dao->get(array(
            'is_delete' => UNDELETED,
            'admin_id'  => $admin_id,
        ));
        if( ! $role_ids )
        {
            return $resource;
        }

        // 获取角色
        $role_ids = array_pick($role_ids, "role_id");
        $this->load->dao('Role_dao');
        $roles = $this->Role_dao->get_in(
            array('role_id'=> $role_ids),
            array('status' => ROLE_STATUS_ON, 'is_delete' => UNDELETED),
            'role_id',
            NULL
        );
        if( ! $roles )
        {
            return $resource;
        }

        // 获取角色权限
        $roles = array_pick($roles, "role_id");
        $this->load->dao('Role_priv_dao');
        $role_priv = $this->Role_priv_dao->get_in(
            array('role_id'   => $roles),
            array('is_delete' => UNDELETED),
            '*',
            NULL
        );
        if( ! $role_priv )
        {
            return $resource;
        }

        // 获取权限资源
        $resource_ids = array_pick($role_priv, "resource_id");
        $this->load->dao('Role_resource_dao');
        $temp_resource = $this->Role_resource_dao->get_in(
            array('resource_id' => $resource_ids),
            array('is_delete'   => UNDELETED),
            '*',
            NULL
        );
        if( ! $temp_resource )
        {
            return $resource;
        }
        
        // 添加访问权限
        array_change_key($role_priv,    'resource_id');
        array_change_key($temp_resource, 'resource_id');
        $role_resource_group = $this->config->item('role_resource_group');

        foreach ($role_priv as $r) 
        {
            $key = $r['resource_id'];
            if( ! isset($temp_resource[$key]) )
                continue;

            foreach ($role_resource_group as $k1 => $v1) 
            {
                $col = strtolower($k1)."_priv";
                if( $r[$col] === ROLE_PRIV_ON )
                {
                    $col  = strtolower($k1)."_actions";
                    $temp = array_explode(",", $temp_resource[$key][$col], TRUE);
                    foreach ($temp as $v2) 
                    {
                        $url  = "{$temp_resource[$key]['controller']}/{$v2}";
                        $url .= $temp_resource[$key]['directory'] ? "{$temp_resource[$key]['directory']}/" : "" ;

                        if( ! isset($resource['priv'][$url]) )
                            $resource['priv'][$url] = TRUE;
                    }
                }
            }
        }

        // 获取菜单资源
        $menu  = array();
        $depth = self::$_max_depth;

        $this->load->dao('Admin_menu_dao');
        $result = $this->Admin_menu_dao->get(
            array('depth <=' => $depth,'is_delete' => UNDELETED),
            array('admin_menu_id','name','depth','parent_id','url','class'),
            '`depth` ASC, `sort` ASC'
        );
        foreach($result as $r)
        {
            $menu[$r['depth']][$r['admin_menu_id']] = $r;
        }

        while($depth>=1)
        {
            // 最底层 结束
            if($depth === 1)
            {
                $resource['menu'] = $menu[1];
                break;
            }

            foreach ($menu[$depth] as $k => $t)
            {
                // 带有链接，检测是否有访问权限
                if( $t['url'])
                {
                    $pu  = parse_url($t['url']);
                    $url = trim($pu['path'], "/");
                    if( FALSE === strpos($url,"/") )
                    {
                        $url .= "/index";
                    }
                    if( ! $resource['priv'][$url] )
                    {
                        continue;
                    }
                }
                else
                {
                    // 既没有链接也没有子菜单，无效
                    if( ! $t['children'] )
                    {
                        continue;
                    }
                }

                // 有效则推入上一层
                $mid = $t['admin_menu_id'];
                $pid = $t['parent_id'];
                if( isset($menu[$depth-1][$pid]) )
                {
                    $menu[$depth-1][$pid]['children'][$mid] = $t;
                }
            }
            unset($menu[$depth]);
            $depth--;
        }
        return $resource;
    }    

    /**
     * [获得一个用户的资料]
     * @param  [int]  $admin_id
     * @param  [bool] $enable        [是否排除"关闭状态"的用户]
     * @param  [bool] $with_priv     [是否包含用户权限]
     * @param  [bool] $with_role     [是否包含用户角色]
     * @return [array/false]
     */
    public function get_admin($admin_id, $enable = TRUE, $with_priv = FALSE, $with_role = FALSE)
    {
        $admin_id = (int)$admin_id;
        if( $admin_id<=0)
        {
            $this->set_error("获取用户数据失败：必须指定一个有效的用户id。");
            return FALSE;
        }
        
        $where = array(
            'admin_id'  => $admin_id,
            'is_delete' => UNDELETED,
        );
        if($enable === TRUE)
        {
            $where['status'] = ADMIN_STATUS_ON;
        }

        $this->load->dao('Admin_dao');
        $admin = $this->Admin_dao->get_one($where);
        if( ! $admin)
        {
            $this->set_error("获取用户数据失败：无法找到用户[{$admin_id}]。");
            return FALSE;
        }

        if( $with_priv === TRUE )
        {
            // 获取用户角色
            $this->load->dao('Role_admin_dao');
            $role_ids = $this->Role_admin_dao->get(array(
                'is_delete' => UNDELETED,
                'admin_id'  => $admin_id,
            ));

            // 获取角色
            if( $role_ids )
            {
                $role_ids = array_pick($role_ids, "role_id");

                $this->load->dao('Role_dao');
                $roles = $this->Role_dao->get_in(
                    array('role_id' => $role_ids),
                    array('status'  => ROLE_STATUS_ON, 'is_delete' => UNDELETED),
                    'role_id',
                    NULL
                );
            }

            // 获取角色所有资源
            if( $roles )
            {
                $roles               = array_pick($roles, "role_id");
                $role_resource_group = $this->config->item('role_resource_group');
                $cols                = array('resource_id');

                foreach($role_resource_group as $k => $v) 
                {
                    $cols[] = strtolower($k)."_priv";
                }

                $this->load->dao('Role_priv_dao');
                $role_privs = $this->Role_priv_dao->get_in(
                    array('role_id' => $roles),
                    array('is_delete' => UNDELETED),
                    $cols,
                    NULL
                );
                array_change_key($role_priv, 'resource_id');
                $admin['privs'] = $role_privs;
            }
        }

        if( $with_role === TRUE )
        {
            $this->load->dao('Role_admin_dao');
            $roles = $this->Role_admin_dao->get(
                array('is_delete' => UNDELETED, 'admin_id' => $admin_id),
                array('role_id')
            );
            array_change_key($roles, 'role_id');
            $admin['roles'] = $roles;
        }

        return $admin;
    }

    /**
     * [获得一个角色]
     * @param  [int]  $admin_id
     * @param  [bool] $enable        [是否排除"关闭状态"的用户]
     * @param  [bool] $with_priv     [是否包含角色权限信息]
     * @param  [bool] $with_member   [是否包含角色成员信息]
     * @return [array/false]
     */
    public function get_role($role_id, $enable = TRUE, $with_priv = FALSE, $with_member = FALSE)
    {
        $role_id = (int)$role_id;
        if( $role_id<=0)
        {
            $this->set_error("获取角色失败：必须提供一个有效的角色id。");
            return FALSE;
        }
        
        $where = array(
            'role_id'   => $role_id,
            'is_delete' => UNDELETED,
        );
        if($enable === TRUE)
        {
            $where['status'] = ROLE_STATUS_ON;
        }

        $this->load->dao('Role_dao');
        $role = $this->Role_dao->get_one($where);
        if( ! $role)
        {
            $this->set_error("获取角色失败：无法找到角色[{$role_id}]。");
            return FALSE;
        }

        if( $with_priv === TRUE )
        {
            // 获取角色权限
            $role_resource_group = $this->config->item('role_resource_group');
            $cols                = array('resource_id');

            foreach($role_resource_group as $k => $v) 
            {
                $cols[] = strtolower($k)."_priv";
            }

            $this->load->dao('Role_priv_dao');
            $role_privs = $this->Role_priv_dao->get(
                array('is_delete' => UNDELETED, 'role_id' => $role_id),
                $cols
            );
            array_change_key($role_priv, 'resource_id');
            $role['privs'] = $role_privs;
        }

        if( $with_member === TRUE )
        {
            $this->load->dao('Role_admin_dao');
            $admins = $this->Role_admin_dao->get(
                array('is_delete' => UNDELETED, 'role_id' => $role_id),
                array('admin_id')
            );
            array_change_key($admins, 'admin_id');
            $role['admins'] = $admins;
        }

        return $role;
    }

    // --------------------------------------------------------------------
    
    /**
     * [密码加密]
     * @param  [string] $password
     * @return [string]
     */
    protected function _encrypt_password($password)
    {
        return sha1(sha1(strtolower($password).self::$_salt));
    }

    /**
     * 验证用户名格式
     * 
     * @param  [string] 
     * @return [bool]
     */
    public function val_username($username)
    {
        if( ! preg_match('/^[a-zA-Z0-9]{4,20}$/', $username) )
        {
            $this->set_error("用户名只能包含英文或数字, 4~20位。");
            return FALSE;
        }
        return TRUE;
    }

    /**
     * 验证密码格式
     * 
     * @param  [string] 
     * @return [bool]
     */
    public function val_password($password)
    {
        if( ! preg_match('/^[a-zA-Z0-9]{6,20}$/', $password) )
        {
            $this->set_error("密码只能包含英文或数字, 6~20位。");
            return FALSE;
        }
        return TRUE;
    }

    // --------------------------------------------------------------------

    /**
     * [新增用户]
     * @param  [array]  $params [所有信息的数组]
     * @return [bool]
     */
    public function add_admin(Array $params)
    {
        $params = array(
            'username'         => $params['username']         ? xss_clean(trim($params['username']))     : NULL,
            'nickname'         => $params['nickname']         ? xss_clean(trim($params['nickname']))     : NULL,
            'gender'           => $params['gender']           ? strtoupper(xss_clean($params['gender'])) : NULL,
            'email'            => $params['email']            ? xss_clean(trim($params['email']))        : NULL,
            'password'         => $params['password']         ? xss_clean($params['password'])           : NULL,
            'confirm_password' => $params['confirm_password'] ? xss_clean($params['confirm_password'])   : NULL,
            'roles'            => $params['roles']            ? xss_clean($params['roles'])              : NULL,
        );
        
        $admin    = array();
        $admin_id = NULL;

        // 校验用户名
        if( ! $this->val_username($params['username']) )
        {
            $this->set_error("新增用户失败：".$this->get_error());
            return FALSE;
        }
        // 校验用户名是否存在
        $this->load->dao('Admin_dao');
        $count = $this->Admin_dao->count(array(
            'username' => $params['username'],
            'is_delete'=> UNDELETED,
        ));
        if( $count > 0 )
        {
            $this->set_error("新增用户失败：用户名已存在");
            return FALSE;
        }
        $admin['username'] = $params['username'];

        // 校验昵称
        if( ! validate_length($params['nickname'], 1, 20) )
        {
            $this->set_error("新增用户失败：昵称最少1个字最多20个字[{$params['nickname']}]。");
            return FALSE;
        }
        $this->load->dao('Admin_dao');
        $count = $this->Admin_dao->count(array(
            'nickname' => $params['nickname'],
            'is_delete'=> UNDELETED,
        ));
        if( $count > 0 )
        {
            $this->set_error("新增用户失败：昵称已存在");
            return FALSE;
        }
        $admin['nickname'] = $params['nickname'];

        // 校验性别
        $admin_gender = $this->config->item('admin_gender');
        if( ! isset($admin_gender[$params['gender']]))
        {
            $this->set_error("新增用户失败：未知的性别[{$params['gender']}]。");
            return FALSE;
        }
        $admin['gender'] = $params['gender'];

        // 校验email
        if( ! validate($params['email'], 'EMAIL') )
        {
            $this->set_error("新增用户失败：邮箱格式不符合规范[{$params['email']}]。");
            return FALSE;
        }
        $admin['email'] = $params['email'];

        // 校验密码
        if( ! $this->val_password($params['password']) )
        {
            $this->set_error("新增用户失败：".$this->get_error());
            return FALSE;
        }
        if( $params['password'] !== $params['confirm_password'])
        {
            $this->set_error("新增用户失败：两次输入的密码不一致。");
            return FALSE;
        }
        $admin['password'] = $this->_encrypt_password($params['password']);

        // 校验用户角色
        if( $params['roles'] !== NULL )
        {
            if( ! is_array($params['roles']) )
            {
                $this->set_error("新增用户失败：角色参数不正确。");
                return FALSE;
            }

            $role_admin = array();
            $this->load->dao('Role_dao');
            foreach ($params['roles'] as $p) 
            {
                $count = $this->Role_dao->count(array(
                    'is_delete' => UNDELETED,
                    'status'    => ROLE_STATUS_ON,
                    'role_id'   => (int)$p,
                ));
                if( $count <= 0)
                {
                    $this->set_error("新增用户失败：无法找到指定的角色[{$p}]。");
                    return FALSE;
                }

                $role_admin[] = array(
                    'admin_id'     => & $admin_id,
                    'role_id'      => (int)$p,
                    'create_admin' => login_info('admin_id'),
                    'create_time'  => now_str(),
                );
            }
        }

        // 新建用户
        $admin['status']       = ADMIN_STATUS_ON;
        $admin['is_delete']    = UNDELETED;
        $admin['create_admin'] = login_info('admin_id');
        $admin['create_time']  = now_str();

        $this->load->dao('Admin_dao');
        if( FALSE === $admin_id = $this->Admin_dao->insert($admin))
        {
            $this->set_error("新增用户失败：无法写入数据库。");
            return FALSE;
        }

        // 写入用户角色
        if($role_admin)
        {
            $this->load->dao('Role_admin_dao');
            if( FALSE === $admin_id = $this->Role_admin_dao->insert_batch($role_admin))
            {
                $this->set_error("新增用户失败：无法写入用户角色。");
                return FALSE;
            }
        }
        
        return $admin_id;
    }

    /**
     * [新增角色]
     * @param  [array]  $params [所有信息的数组]
     * @return [bool]
     */
    public function add_role(Array $params)
    {
        $params = array(
            'name'   => $params['name']   ? xss_clean(trim($params['name'])) : NULL,
            'privs'  => $params['privs']  ? $params['privs']                 : NULL,
            'admins' => $params['admins'] ? $params['admins']                : NULL,
        );

        // 校验角色名
        if( ! validate_length($params['name'], 1, 20) )
        {
            $this->set_error("新增角色失败：角色名称最少1个字最多20个字[{$params['name']}]。");
            return FALSE;
        }
        // 校验角色名是否存在
        $this->load->dao('Role_dao');
        $count = $this->Role_dao->count(array(
            'name'      => $params['name'],
            'status'    => ROLE_STATUS_ON,
            'is_delete' => UNDELETED,
        ));
        if( $count > 0 )
        {
            $this->set_error("新增角色失败：角色名已存在。");
            return FALSE;
        }

        $role['name']         = $params['name'];
        $role['status']       = ROLE_STATUS_ON;
        $role['is_delete']    = UNDELETED;
        $role['create_admin'] = login_info('admin_id');
        $role['create_time']  = now_str();

        $this->load->dao('Role_dao');
        if( FALSE === $role_id = $this->Role_dao->insert($role))
        {
            $this->set_error("新增角色失败：无法写入数据库。");
            return FALSE;
        }

        // 添加权限
        if( is_array($params['privs']) )
        {
            if( FALSE === $this->edit_role_privs($role_id, $params['privs']))
                return FALSE;
        }

        // 添加用户
        if( is_array($params['admins']) )
        {
            $this->load->dao('Admin_dao');
            $this->load->dao('Role_admin_dao');

            $data = array();
            foreach ($params['admins'] as $a) 
            {
                $admin_id = (int)$a;
                $count = $this->Admin_dao->count(array(
                    'admin_id'  => $admin_id,
                    'is_delete' => UNDELETED,
                    'status'    => ADMIN_STATUS_ON,
                ));
                if( $count <= 0 )
                {
                    $this->set_error("编辑角色用户失败：用户id不正确[{$a}]。");
                    return FALSE;
                }
                $data[] = array(
                    'admin_id' => $admin_id,
                    'role_id'  => $role_id,
                );
            }
            if( FALSE === $this->Role_admin_dao->replace_batch($data) )
            {
                $this->set_error("新增角色失败：无法写入数据库。");
                return FALSE;
            }
        }

        return $role_id;
    }

    // --------------------------------------------------------------------
    
    /**
     * [编辑一个角色基本信息]
     * @param  [int]    $role_id  [角色id]
     * @param  [array]  $params   
     * @return [bool]
     */
    public function edit_role($role_id, Array $params)
    {
        $role_id = (int)$role_id;
        if( FALSE === $role = $this->get_role($role_id, TRUE) )
        {
            $this->set_error("编辑角色失败：无法找到角色。");
            return FALSE;
        }

        $params = array(
            'name' => $params['name'] ? xss_clean(trim($params['name'])) : NULL,
        );
        
        $role = array();

        // 校验昵称
        if( ! validate_length($params['name'], 1, 20) )
        {
            $this->set_error("编辑角色失败：角色名最少1个字最多20个字[{$params['name']}]。");
            return FALSE;
        }
        $this->load->dao('Role_dao');
        $count = $this->Role_dao->count(array(
            'name'       => $params['name'],
            'is_delete'  => UNDELETED,
            'role_id <>' => $role_id,
        ));
        if( $count > 0 )
        {
            $this->set_error("编辑角色失败：角色名[{$params['name']}]已存在");
            return FALSE;
        }
        $role['name'] = $params['name'];

        // 写入
        $role['update_admin'] = login_info('admin_id');
        $role['update_time']  = now_str();

        $where['role_id']   = $role_id; 
        $where['status']    = ROLE_STATUS_ON; 
        $where['is_delete'] = UNDELETED;

        $this->load->dao('Role_dao');
        $count = $this->Role_dao->update(
            $role,
            $where,
            NULL,
            1
        );
        if( ! ($count > 0) )
        {
            $this->set_error("编辑角色失败：无法写入数据库。");
            return FALSE;
        }

        return TRUE;
    }

    /**
     * [编辑一个角色拥有的权限资源]
     * @param  [int]    $role_id [角色id]
     * @param  [array]  $privs   [权限数组: {资源id}-{操作类型}；eg.array(0=>'1-EDIT')]
     * @return [bool]
     */
    public function edit_role_priv($role_id, Array $privs)
    {
        $role_id = (int)$role_id;
        if( FALSE === $role = $this->get_role($role_id, TRUE) )
        {
            $this->set_error("编辑角色权限失败：无法找到角色[{$role_id}]。");
            return FALSE;
        }

        $role_resource_group = $this->config->item('role_resource_group');
        $rg_str = '';
        foreach ($role_resource_group as $k => $v) 
        {
            $rg_str .= "{$k}|";
        }
        $rg_str = rtrim($rg_str, '|');

        $this->load->dao('Role_resource_dao');
        $data = array();
        foreach ($privs as $p) 
        {
            $pattern = "/^(?<id>[\d]{1,})-(?<priv>$rg_str)$/";
            if( ! preg_match($pattern, $p, $matches) )
            {
                $this->set_error("编辑角色权限失败：权限参数格式不正确[{$p}]。");
                return FALSE;
            }

            $count = $this->Role_resource_dao->count(array(
                'resource_id' => $matches['id'],
                'is_delete'   => UNDELETED
            ));

            if( $count <= 0 )
            {
                $this->set_error("编辑角色权限失败：权限资源不存在[{$p}]。");
                return FALSE;
            }
            $data[$matches['id']][strtolower($matches['priv']).'_priv'] = ROLE_PRIV_ON;
        }

        // 整理插入数据
        foreach ($data as $k => $v) 
        {
            foreach ($role_resource_group as $rg => $rgv) 
            {
                $data[$k]['resource_id']  = $k;
                $data[$k]['role_id']      = $role_id;
                $data[$k]['create_time']  = now_str();
                $data[$k]['create_admin'] = login_info('admin_id');

                $col = strtolower($rg)."_priv";
                if( ! isset($data[$k][$col]) )
                    $data[$k][$col] = ROLE_PRIV_OFF;
            }
        }

        $this->load->dao('Role_priv_dao');
        if( FALSE === $this->Role_priv_dao->delete(
            array('role_id' => $role_id)
        ))
        {
            $this->set_error("编辑角色权限失败：无法写入数据库。");
            return FALSE;
        }

        $count = $this->Role_priv_dao->insert_batch($data);
        if($count<=0)
        {
            $this->set_error("编辑角色权限失败：无法写入数据库。");
            return FALSE;
        }
        return TRUE;
    }

    /**
     * [编辑一个角色拥有的成员]
     * @param  [int]    $role_id [角色id]
     * @param  [array]  $admins  [成员数组: eg.array(0=>2)]
     * @return [bool]
     */
    public function edit_role_member($role_id, Array $admins)
    {
        $role_id = (int)$role_id;
        if( FALSE === $role = $this->get_role($role_id, TRUE) )
        {
            $this->set_error("编辑角色成员失败：无法找到角色。");
            return FALSE;
        }
        
        $data = array();
        $this->load->dao('Admin_dao');
        foreach ($admins as $a) 
        {
            $aid = (int)$a;
            $count = $this->Admin_dao->count(array(
                'is_delete' => UNDELETED,
                'status'    => ADMIN_STATUS_ON
            ));

            if( ! ($count > 0) )
            {
                $this->set_error("编辑角色成员失败：无法找到用户[{$a}}。");
                return FALSE;
            }
            $data[] = array(
                'role_id'     => $role_id,
                'admin_id'    => $aid,
                'create_time' => now_str(),
                'is_delete'   => UNDELETED,
            );
        }
        
        $this->load->dao('Role_admin_dao');
        $this->Role_admin_dao->update(
            array('is_delete'=>DELETED),
            array('role_id'=>$role_id)
        );
        if( FALSE === $this->Role_admin_dao->replace_batch($data) )
        {
            $this->set_error("编辑角色成员失败：无法写入数据库。");
            return FALSE;
        }
        return TRUE;
    }

    /**
     * [编辑一个用户的基本资料]
     * @param  [array]  $params [所有用户信息的数组]
     * @return [bool]
     */
    public function edit_admin(Array $params)
    {
        $admin_id = (int)$params['admin_id'];
        $params = array(
            'nickname' => $params['nickname'] ? xss_clean(trim($params['nickname']))            : NULL,
            'gender'   => $params['gender']   ? strtoupper(xss_clean(trim($params['gender'])))  : NULL,
            'email'    => $params['email']    ? xss_clean(trim($params['email']))               : NULL,
        );

        $admin = array();

        // 校验昵称
        if( isset($params['nickname']) )
        {
            if( ! validate_length($params['nickname'], 1, 20) )
            {
                $this->set_error("编辑用户失败：昵称最少1个字最多20个字[{$params['nickname']}]。");
                return FALSE;
            }
            $admin['nickname'] = $params['nickname'];
        }

        // 校验性别
        if( isset($params['gender']) )
        {
            $admin_gender = $this->config->item('admin_gender');
            if( ! isset($admin_gender[$params['gender']]))
            {
                $this->set_error("编辑用户失败：未知的性别[{$params['gender']}]。");
                return FALSE;
            }
            $admin['gender'] = $params['gender'];
        }

        // 校验email
        if( isset($params['email']) )
        {
            if( ! validate($params['email'], 'EMAIL') )
            {
                $this->set_error("编辑用户失败：邮箱格式不符合规范[{$params['email']}]。");
                return FALSE;
            }
            $admin['email'] = $params['email'];
        }

        if( ! $admin)
        {
            $this->set_error("编辑用户失败：没有需要编辑的数据。");
            return FALSE;
        }

        $this->load->dao('Admin_dao');
        if( FALSE === $this->Admin_dao->update(
            $admin,
            array(
                'admin_id'     => $admin_id, 
                'is_delete'    => UNDELETED, 
                'update_admin' => login_info('admin_id'), 
                'update_time'  => now_str()
            ),
            NULL,
            1
        ))
        {
            $this->set_error("编辑用户失败：无法写入数据库。");
            return FALSE;
        }

        return TRUE;
    }

    /**
     * [编辑一个用户拥有的角色]
     * @param  [int]    $admin_id   [用户id]
     * @param  [array]  $roles      [角色id数组 eg.array(1,5)]
     * @return [bool]
     */
    public function edit_admin_role($admin_id, Array $roles)
    {
        $admin_id = (int)$admin_id;
        if( FALSE === $admin = $this->get_admin($admin_id, TRUE) )
        {
            $this->set_error("编辑用户角色失败：无法找到用户[{$admin_id}]。");
            return FALSE;
        }
        
        $data = array();
        $this->load->dao('Role_dao');
        foreach ($roles as $r) 
        {
            $rid = (int)$r;
            $count = $this->Role_dao->count(array(
                'is_delete' => UNDELETED,
                'status'    => ROLE_STATUS_ON
            ));

            if( ! ($count > 0) )
            {
                $this->set_error("编辑用户角色失败：无法找到角色[{$r}}。");
                return FALSE;
            }
            $data[] = array(
                'admin_id'    => $admin_id,
                'role_id'     => $rid,
                'create_time' => now_str(),
            );
        }

        $this->load->dao('Role_admin_dao');
        if( FALSE === $this->Role_admin_dao->replace_batch($data) )
        {
            $this->set_error("编辑用户角色失败：无法写入数据库。");
            return FALSE;
        }
        return TRUE;
    }

    // --------------------------------------------------------------------
    
    /**
     * [删除角色权限]
     * @param  [int]    $role_id [角色id]
     * @return [bool]
     */
    public function del_role_priv($role_id)
    {
        $role_id = (int)$role_id;
        if( FALSE === $role = $this->get_role($role_id) )
        {
            $this->set_error("删除角色权限失败：无法找到角色[{$role_id}]。");
            return FALSE;
        }

        $this->load->dao('Role_priv_dao');
        if( FALSE === $this->Role_priv_dao->delete( array('role_id' => $role_id) ) )
        {
            return FALSE;
        }
        return TRUE;
    }

    /**
     * [删除角色成员]
     * @param  [int]    $role_id [角色id]
     * @return [bool]
     */
    public function del_role_member($role_id)
    {
        $role_id = (int)$role_id;
        if( FALSE === $role = $this->get_role($role_id) )
        {
            $this->set_error("删除角色成员失败：无法找到角色[{$role_id}]。");
            return FALSE;
        }

        $this->load->dao('Role_admin_dao');
        if( FALSE === $this->Role_admin_dao->delete( array('role_id' => $role_id) ) )
        {
            return FALSE;
        }
        return TRUE;
    }

    /**
     * [删除用户拥有的角色]
     * @param  [int]    $admin_id [用户id]
     * @return [bool]
     */
    public function del_admin_role($admin_id)
    {
        $admin_id = (int)$admin_id;
        if( FALSE === $admin = $this->get_admin($admin_id, TRUE) )
        {
            $this->set_error("删除用户角色失败：无法找到用户[{$admin_id}]。");
            return FALSE;
        }

        $this->load->dao('Role_admin_dao');
        if( FALSE === $this->Role_admin_dao->delete( array('admin_id' => $admin_id) ) )
        {
            return FALSE;
        }
        return TRUE;
    }

    // --------------------------------------------------------------------
    
    /**
     * [更新用户头像版本]
     * @param  [int] $admin_id
     * @return [bool]
     */
    public function update_avatar_version($admin_id)
    {
        $admin_id = (int)$admin_id;
        $this->load->dao('Admin_dao');

        $version = time();
        $count = $this->Admin_dao->update(
            array('avatar_version' => $version),
            array(
                'admin_id'  => $admin_id,
                'status'    => ADMIN_STATUS_ON,
                'is_delete' => UNDELETED
            ),
            NULL,
            1
        );

        if((int)$count !== 1)
        {
            $this->set_error("更新头像版本失败。");
            return FALSE;
        }

        $_SESSION['login_info']['avatar_version'] = $version;

        return TRUE;
    }

    // --------------------------------------------------------------------

    /**
     * [分页浏览]
     * @param  [array]  $params  [条件]
     * @param  [int]    $page    [当前页]
     * @param  [int]    $size    [页数，只允许app_config中"pagesize"的值]
     * @param  [string] $orderby [排序]
     * @return [array]
     */
    public function browse_admin($params, $page = NULL, $size = NULL, $orderby = '`admin_id` DESC')
    {
        $output = array(
            'size' => 0,
            'data' => array(),
            'total'=> 0,
        );

        $where = array(
            'is_delete' => UNDELETED,
        );
        $where_in = NULL;

        if( ! empty($params['username']) )
        {
            $where['`username` LIKE'] = "%".xss_clean(trim($params['username']))."%";
        }

        if( ! empty($params['nickname']) )
        {
            $where['`nickname` LIKE'] = "%".xss_clean(trim($params['nickname']))."%";
        }

        if( ! empty($params['gender']) )
        {
            $admin_gender = $this->config->item('admin_gender');
            if( $admin_gender["{$params['gender']}"] )
                $where['gender'] = $params['gender'];
        }

        if( ! empty($params['status']) )
        {
            $admin_status = $this->config->item('admin_status');
            if( $admin_status["{$params['status']}"] )
                $where['status'] = $params['status'];
        }

        if( ! empty($params['role']) )
        {
            $this->load->dao('Role_admin_dao');
            $admin_ids = $this->Role_admin_dao->get(
                array(
                    'is_delete' => UNDELETED,
                    'role_id'   => (int)$params['role'],
                ),
                array(
                    'admin_id'
                )
            );
            $admin_ids = array_pick($admin_ids, 'admin_id');
            if($admin_ids)
                return $output;

            $where_in['admin_id'] = $admin_ids;
        }

        if( ! empty($params['create_time_start']) && strtotime($params['create_time_start']) )
        {
            $where['`create_time` >'] = $params['create_time_start'];
        }

        if( ! empty($params['create_time_end']) && strtotime($params['create_time_end']) )
        {
            $where['`create_time` <'] = $params['create_time_end'];
        }

        if( ! empty($params['update_time_start']) && strtotime($params['update_time_start']) )
        {
            $where['`update_time` >'] = $params['update_time_start'];
        }

        if( ! empty($params['update_time_end']) && strtotime($params['update_time_end']) )
        {
            $where['`update_time` <'] = $params['update_time_end'];
        }

        $pagesize = $this->config->item('pagesize');
        $size     = isset($pagesize[$size]) ? $size : 10;
        $offset   = intval(($page-1)*$size) > 0 ? intval(($page-1)*$size) : 0;
        $total    = 0;

        $this->load->dao('Admin_dao');
        $data = $this->Admin_dao->get_in(
            $where_in,
            $where, 
            '*', 
            $orderby, 
            $size, 
            $offset, 
            TRUE, 
            $total
        );
        
        $output['size']  = $size; 
        $output['data']  = $data; 
        $output['total'] = $total;
        
        return $output;
    }

    /**
     * [分页浏览]
     * @param  [array]  $params  [条件]
     * @param  [int]    $page    [当前页]
     * @param  [int]    $size    [页数，只允许app_config中"pagesize"的值]
     * @param  [string] $orderby [排序]
     * @return [array]
     */
    public function browse_role($params, $page = NULL, $size = NULL, $orderby = '`role_id` DESC')
    {
        $where = array(
            'is_delete' => UNDELETED,
        );

        if( ! empty($params['name']) )
        {
            $where['`name` LIKE'] = "%".xss_clean(trim($params['name']))."%";
        }

        if( ! empty($params['status']) )
        {
            $role_status = $this->config->item('role_status');
            if( $role_status["{$params['status']}"] )
                $where['status'] = $params['status'];
        }

        if( ! empty($params['create_time_start']) && strtotime($params['create_time_start']) )
        {
            $where['`create_time` >'] = $params['create_time_start'];
        }

        if( ! empty($params['create_time_end']) && strtotime($params['create_time_end']) )
        {
            $where['`create_time` <'] = $params['create_time_end'];
        }

        if( ! empty($params['update_time_start']) && strtotime($params['update_time_start']) )
        {
            $where['`update_time` >'] = $params['update_time_start'];
        }

        if( ! empty($params['update_time_end']) && strtotime($params['update_time_end']) )
        {
            $where['`update_time` <'] = $params['update_time_end'];
        }

        $pagesize = $this->config->item('pagesize');
        $size     = isset($pagesize[$size]) ? $size : 10;
        $offset   = intval(($page-1)*$size) > 0 ? intval(($page-1)*$size) : 0;
        $total    = 0;

        $this->load->dao('Role_dao');
        $data = $this->Role_dao->get(
            $where, 
            '*', 
            $orderby, 
            $size, 
            $offset, 
            TRUE, 
            $total
        );
        return array(
            'size' => $size,
            'data' => $data,
            'total'=> $total,
        );
    }

    // --------------------------------------------------------------------
    
    /**
     * [绑定用户的成员数量]
     * @param  [& array]    列表, 必须包含admin_id
     * @param  [bool]       是否开启单个模式
     * @param  [string]     列表中`admin_id`列的键名
     * @return [bool]
     */
    public function wrap_role_member_count( & $list, $single_mode = FALSE, $key = 'role_id')
    {
        if( ! $list || ! $key ) 
            return FALSE;
        
        if($single_mode === TRUE)
        {
            $id = $list[$key];
            if( ! $id) 
                return FALSE;

            $this->load->dao('Role_admin_dao');
            $list['role_member_count'] = $this->Role_admin_dao->count(array(
                'role_id' => $id
            ));
        }
        else
        {
            $ids = array_pick($list, $key);
            if( ! $ids) 
                return FALSE;

            $this->load->dao('Role_admin_dao');
            $counts = $this->Role_admin_dao->get_role_member_count($ids);
            
            foreach ($list as &$item)
            {
                if( isset($counts[$item[$key]]) )
                {
                    $item['role_member_count'] = $counts[$item[$key]]['count'];
                }
                else
                {
                    $item['role_member_count'] = 0;
                }
            }
        }
        
        unset($list);
        return TRUE;
    }

    /**
     * [绑定用户的角色的信息]
     * @param  [& array]    列表, 必须包含admin_id
     * @param  [bool]       是否开启单个模式
     * @param  [string]     列表中`admin_id`列的键名
     * @return [bool]
     */
    public function wrap_role_info( & $list, $single_mode = FALSE, $key = 'admin_id')
    {
        if( ! $list || ! $key ) 
            return FALSE;
        
        if($single_mode === TRUE)
        {
            $id = $list[$key];
            if( ! $id) 
                return FALSE;

            // 获取用户角色
            $this->load->dao("Role_admin_dao");     
            $role_admin_ids = $this->Role_admin_dao->get(
                array('is_delete' => UNDELETED, 'admin_id'=>$id), 
                array('role_id','admin_id')
            ); 


            // 获取角色信息
            if( $role_admin_ids )
            {
                $role_ids = array_pick($role_admin_ids, 'role_id', TRUE);
                $this->load->dao("Role_dao");    
                $roles = $this->Role_dao->get_in(
                    array('role_id'   => $role_ids),
                    array('is_delete' => UNDELETED), 
                    array('role_id','name','status')
                );
                array_change_key($roles, 'role_id');

                $temp = array();
                foreach ($role_admin_ids as $r) 
                {
                    $aid = $r['admin_id'];
                    $rid = $r['role_id'];

                    if( isset($roles[$rid]) )
                        $temp[$aid][$rid] = $roles[$rid];
                }

                if( isset($temp[$id]) )
                {
                    $list['roles_info'] = $temp[$id];
                }
                else
                {
                    $list['roles_info'] = array();
                }
            }
        }
        else
        {
            $ids = array_pick($list, $key);
            if( ! $ids) 
                return FALSE;
            
            // 获取用户角色
            $this->load->dao("Role_admin_dao");     
            $role_admin_ids = $this->Role_admin_dao->get_in(
                array('admin_id'  => $ids), 
                array('is_delete' => UNDELETED), 
                array('role_id','admin_id')
            );  

            // 获取角色信息
            if( $role_admin_ids )
            {
                $role_ids = array_pick($role_admin_ids, 'role_id', TRUE);
           
                $this->load->dao("Role_dao");    
                $roles = $this->Role_dao->get_in(
                    array('role_id' => $role_ids), 
                    array('is_delete' => UNDELETED), 
                    array('role_id','name','status')
                );
                array_change_key($roles, 'role_id');

                $temp = array();
                foreach ($role_admin_ids as $r) 
                {
                    $aid = $r['admin_id'];
                    $rid = $r['role_id'];

                    if( isset($roles[$rid]) )
                        $temp[$aid][$rid] = $roles[$rid];
                }

                foreach ($list as &$item) 
                {
                    $aid = $item[$key];
                    if( isset($temp[$aid]) )
                    {
                        $item['roles_info'] = $temp[$aid];
                    }
                    else
                    {
                        $item['roles_info'] = array();
                    }
                }
                unset($item);
            }
        }
        
        unset($list);
        return TRUE;
    }

    /**
     * [绑定用户的角色的信息]
     * @param  [& array]    列表, 必须包含admin_id
     * @param  [bool]       是否开启单个模式
     * @param  [string]     列表中`admin_id`列的键名
     * @return [bool]
     */
    public function wrap_admin_info( & $list, $single_mode = FALSE, $key = 'admin_id')
    {
        if( ! $list || ! $key ) 
            return FALSE;
        
        switch ($key) 
        {
            case 'admin_id':
                $col = 'admin_info';
                break;

            case 'create_admin':
                $col = 'create_admin_info';
                break;

            case 'update_admin':
                $col = 'update_admin_info';
                break;
            default:
                return FALSE;
                break;
        }



        if($single_mode === TRUE)
        {
            $id = $list[$key];
            if( ! $id) 
                return FALSE;

            $this->load->dao('Admin_dao');
            $admin = $this->Admin_dao->get_one(
                array(
                    'is_delete' => UNDELETED,
                    'admin_id'  => $id,
                ),
                array(
                    'nickname'
                )
            );
            $list[$col] = $admin;
        }
        else
        {
            $ids = array_pick($list, $key);
            if( ! $ids) 
                return FALSE;
            
            // 获取角色信息
            $this->load->dao("Admin_dao");    
            $admin = $this->Admin_dao->get_in(
                array('admin_id' => $ids),
                array('is_delete' => UNDELETED), 
                array('nickname')
            );

            array_change_key($admin, 'admin_id');

            foreach ($list as &$item) 
            {
                if( isset($admin[$item[$key]]) )
                {
                    $item[$col] = $admin[$item[$key]];
                }
                else
                {
                    $item[$col] = array();
                }
            }
            unset($item);
        }
        
        unset($list);
        return TRUE;
    }

    // --------------------------------------------------------------------

    /**
     * [改变用户状态]
     * 
     * @param  [int]    $admin_id   [编号]
     * @param  [string] $act        [操作类型]
     * @return [bool]
     */
    public function change_admin_status($admin_id, $act)
    {   
        if( ! $admin = $this->get_admin($admin_id, FALSE))
        {
            $this->set_error("修改用户状态失败：无法找到用户[{$id}]。");
            return FALSE;
        }

        // 初始化数据
        $_status       = NULL;  //修改的状态
        $_allow_status = NULL;  //允许修改的状态
        $_update       = NULL;  //更新的数据
        
        switch (strtolower(trim($act)))
        {
            case 'on':
                    $_status       = ADMIN_STATUS_ON;
                    $_allow_status = array(ADMIN_STATUS_OFF);
                    $_update       = array(
                        'status'       => $_status,
                        'update_time'  => now_str(),
                        'update_admin' => login_info('admin_id'), 
                    );
                break;

            case 'off':
                    $_status       = ADMIN_STATUS_OFF;
                    $_allow_status = array(ADMIN_STATUS_ON);
                    $_update       = array(
                        'status'       => $_status,
                        'update_time'  => now_str(),
                        'update_admin' => login_info('admin_id'), 
                    );
                break;

            default:
                    $this->set_error("修改用户状态失败：未知的操作[{$act}]。");
                    return FALSE;
                break;
        }

        $status      = $this->config->item('admin_status');
        $status_name = (string)$status[$_status];

        // 验证状态
        $current_status = (int)$admin['status'];
        if($current_status === $_status)
        {
            $this->set_error("修改用户状态失败：无需修改。");
            return FALSE;
        }

        if( $_allow_status && ! in_array($current_status, $_allow_status))
        {
            $this->set_error("修改用户状态失败：不允许被修改为[{$status_name}]状态。");
            return FALSE;
        }

       
        // 只有符合条件才会被修改
        $this->load->dao('Admin_dao');
        $affected_row = $this->Admin_dao->update_in(
            $_update,
            array('status'   => $_allow_status),
            array('admin_id' => $admin_id)
        );
        if($affected_row<=0)
        {
            $this->set_error("修改用户状态失败：无法写入数据库。");
            return FALSE;
        }

        return TRUE;
    }

    /**
     * [改变角色状态]
     * 
     * @param  [int]    $role_id    [编号]
     * @param  [string] $act        [操作类型]
     * @return [bool]
     */
    public function change_role_status($role_id, $act)
    {   
        $role_id = (int)$role_id;

        if( FALSE === $role = $this->get_role($role_id, FALSE) )
        {
            $this->set_error("修改角色状态失败：无法找到角色[{$id}]。");
            return FALSE;
        }

        // 初始化数据
        $_status       = NULL;  //修改的状态
        $_allow_status = NULL;  //允许修改的状态
        $_update       = NULL;  //更新的数据
        
        switch (strtolower(trim($act)))
        {
            case 'on':
                    $_status       = ROLE_STATUS_ON;
                    $_allow_status = array(ROLE_STATUS_OFF);
                    $_update       = array(
                        'status'       => $_status,
                        'update_time'  => now_str(),
                        'update_admin' => login_info('admin_id'), 
                    );
                break;

            case 'off':
                    $_status       = ROLE_STATUS_OFF;
                    $_allow_status = array(ROLE_STATUS_ON);
                    $_update       = array(
                        'status'       => $_status,
                        'update_time'  => now_str(),
                        'update_admin' => login_info('admin_id'), 
                    );
                break;

            default:
                    $this->set_error("修改角色状态失败：未知的操作[{$act}]。");
                    return FALSE;
                break;
        }

        $status      = $this->config->item('role_status');
        $status_name = (string)$status[$_status];

        // 验证状态
        $current_status = (int)$role['status'];
        if($current_status === $_status)
        {
            $this->set_error("修改角色状态失败：无需修改。");
            return FALSE;
        }
        if( $_allow_status && ! in_array($current_status, $_allow_status))
        {
            $this->set_error("修改角色状态失败：不允许被修改为[{$status_name}]状态。");
            return FALSE;
        }

       
        // 只有符合条件才会被修改
        $this->load->dao('Role_dao');
        $affected_row = $this->Role_dao->update_in(
            $_update,
            array('status'  => $_allow_status),
            array('role_id' => $role_id)
        );
        if($affected_row<=0)
        {
            $this->set_error("修改角色状态失败：无法写入数据库。");
            return FALSE;
        }

        return TRUE;
    }

    /**
     * [修改用户密码]
     * @param  [int]    $admin_id
     * @param  [string] $old_password
     * @param  [string] $new_password
     * @return [bool]
     */
    public function change_admin_password($admin_id, $old_password, $new_password)
    {
        // 查找用户
        if( FALSE === $admin = $this->get_admin($admin_id, TRUE) )
        {
            $this->set_error("修改密码失败：无法找到用户[{$admin_id}]。");
            return FALSE;
        }
        // 验证密码
        if( FALSE === $this->val_admin_account($admin['username'], $old_password) )
        {
            $this->set_error("修改密码失败：旧密码错误。");
            return FALSE;
        }

        // 验证新密码
        if( FALSE === $this->val_password($new_password) )
        {
            $this->set_error("修改密码失败：".$this->get_error());
            return FALSE;
        }

        $new_password = $this->_encrypt_password($new_password);
        $this->load->dao('Admin_dao');
        if( 1 !== $this->Admin_dao->update(
            array('password'=>$new_password),
            array('admin_id'=>$admin_id, 'status'=>ADMIN_STATUS_ON, 'is_delete'=>UNDELETED),
            NULL,
            1
        ))
        {
            $this->set_error("修改密码失败：写入数据库失败。");
            return FALSE;
        }

        return TRUE;
    }

}

/* End of file */